﻿using System;
using System.Collections;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Windows.Forms;

namespace Boxie
{
    class BoxieWebLib
    {
        public static Socket socket;
        private static byte[] buffer = new byte[1024];
        private static MemoryStream bufferDrawing = new MemoryStream();
        private static ArrayList alSendQueue;
        private static BinaryWriter bwriter;
        private static string sendingFilePath;
        private static bool isWriting;
        private static bool isSendingDrawingUpdate;
        private static bool isSendingTextUpdate;
        private static StringBuilder buffWaiting = new StringBuilder();
        private static string buffTextUpdate;

        public static void ConnectMode(string ip, int port)
        {
            if (socket != null)
            {
                frmBoxie.instance.fL.add("Socket already in use. Restart boxie to use this command.", 3, "You already issued a LISTEN or CONNECT command. To be able to switch, please restart Boxie and try again.");
                return;
            }
            try
            {
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                frmBoxie.instance.fL.add("Attempting to connect (" + ip + ":" + port.ToString() + ")...", 5);
                socket.Connect(new IPEndPoint(IPAddress.Parse(ip), port));
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnBoxieReceivedData), null);
                frmBoxie.instance.fL.add("Connected!", 5);
            }
            catch(Exception e)
            {
                frmBoxie.instance.fL.add("Could not connect to the remote Boxie.", 3, e.Message);
                socket.Dispose();
                socket = null;
            }
        }

        public static void ListenMode(int port)
        {
            if (socket != null)
            {
                frmBoxie.instance.fL.add("Socket already in use. Restart boxie to use this command.", 3, "You already issued a LISTEN or CONNECT command. To be able to switch, please restart Boxie and try again.");
                return;
            }
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Bind(new IPEndPoint(IPAddress.Any, port));
            socket.Listen(1);
            socket.BeginAccept(new AsyncCallback( OnBoxieConnect ), null);
            frmBoxie.instance.fL.add("Boxie is waiting for a connection (port " + port.ToString() + ")..", 5);
        }

        public static void SendToBoxie(string s)
        {
            byte[] buff = System.Text.Encoding.ASCII.GetBytes(s);
            socket.Send(buff);
        }

        public static void SendBytesToBoxie(byte[] bytes)
        {
            socket.Send(bytes);
        }

        public static void OnBoxieConnect(IAsyncResult ar)
        {
            socket = socket.EndAccept(ar);
            frmLog.add_fromThread("Connection accepted from " + socket.RemoteEndPoint.ToString() + "!", 5);
            socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback( OnBoxieReceivedData ), null);
        }

        public static void OnBoxieReceivedData(IAsyncResult ar)
        {
            try
            {
                int br = socket.EndReceive(ar);
                string msg = System.Text.Encoding.ASCII.GetString(buffer, 0, br);
                if (isWriting)
                {
                    if (msg.Contains("EOF"))
                    {
                        bwriter.Write(buffer, 0, br - 3);
                        isWriting = false;
                        bwriter.Close();
                        frmLog.add_fromThread("File received successfully.", 4);
                        SendToBoxie("NEXT");
                    }
                    else
                        bwriter.Write(buffer, 0, br);
                }
                else if (isSendingDrawingUpdate)
                {
                    if (msg.Contains("EOS"))
                    {
                        bufferDrawing.Write(buffer, 0, buffer.Length - 3); // remove EOS from end
                        isSendingDrawingUpdate = false;
                        BoxieThreadLib.UpdateDrawingPadFromStream(bufferDrawing.ToArray());
                    }
                    else
                    {
                        bufferDrawing.Write(buffer, 0, buffer.Length);
                    }
                }
                else if (isSendingTextUpdate)
                {
                    if (msg.Contains(":EOS!"))
                    {
                        buffTextUpdate += msg;
                        buffTextUpdate = buffTextUpdate.Substring(0, buffTextUpdate.Length - 5);
                        BoxieThreadLib.UpdateTextEditor(buffTextUpdate);
                        isSendingTextUpdate = false;
                        buffTextUpdate = "";
                    }
                    else
                    {
                        buffTextUpdate += msg;
                    }
                }
                else
                {
                    if (msg.Contains("INIT"))
                    {
                        frmLog.add_fromThread("Receiving file: " + msg.Substring(4), 5);
                        bwriter = new BinaryWriter(File.Open(BoxieRegLib.GetSettingString("Internet/SaveFile", Application.StartupPath) + "\\" + msg.Substring(4), FileMode.Append));
                        isWriting = true;
                        SendToBoxie("BEGINSEND");
                    }
                    else if (msg.Contains("BEGINSEND"))
                    {
                        byte[] fileData = File.ReadAllBytes(sendingFilePath);
                        socket.Send(fileData);
                        SendToBoxie("EOF");
                    }
                    else if (msg.Contains("NEXT"))
                    {
                        frmLog.add_fromThread("File sent successfully.", 4);
                        alSendQueue.RemoveAt(0);
                        SendNextInQueue();
                    }
                    else if (msg.Contains("DRAWSTART"))
                    {
                        BoxieThreadLib.ShowDrawingForm(socket.RemoteEndPoint.ToString());
                    }
                    else if (msg.Contains("DRAWCLOSE"))
                    {
                        BoxieThreadLib.HideDrawingForm();
                    }
                    else if (msg.Contains("DRAWUPDT"))
                    {
                        if (msg.Contains("EOS"))
                        {
                            bufferDrawing.Write(buffer, 8, buffer.Length - 11); // remove EOS from end too (8 - 3 = 11)
                            BoxieThreadLib.UpdateDrawingPadFromStream(bufferDrawing.ToArray());
                        }
                        else
                        {
                            bufferDrawing.Write(buffer, 8, buffer.Length - 8); // only remove beginning DRAWUPDT (length of 8)
                            isSendingDrawingUpdate = true; // client is expecting more data
                        }
                    }
                    else if (msg.Contains("TESTART"))
                    {
                        BoxieThreadLib.ShowTextEditorForm();
                    }
                    else if (msg.Contains("TECLOSE"))
                    {
                        BoxieThreadLib.HideTextEditor();
                    }
                    else if (msg.Contains("TEUPDT"))
                    {
                        if (msg.Contains(":EOS!"))
                        {
                            string updatedText = msg.Substring(6);
                            updatedText = updatedText.Substring(0, updatedText.Length - 5);
                            BoxieThreadLib.UpdateTextEditor(updatedText);
                            isSendingTextUpdate = false;
                        }
                        else
                        {
                            buffTextUpdate = msg.Substring(6);
                            isSendingTextUpdate = true;
                        }
                    }
                }

                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(OnBoxieReceivedData), null);
            }
            catch(Exception e)
            {
                frmLog.add_fromThread("Remote Boxie has closed the socket connection.", 3, e.Message);
                socket.Dispose();
                socket = null;
            }
        }

        // ############# BOXIE NON-SOCKET SPECIFIC FUNCTIONS #######################
        public static void SendFilesInQueue()
        {
            // populate ArrayList(alSendQueue)
            alSendQueue = new ArrayList();
            for (int x = 0; x <= frmBoxie.instance.appargs.GetUpperBound(0); x++)
            {
                alSendQueue.Add(frmBoxie.instance.appargs[x]);
            }
            // send the first file
            SendNextInQueue();
        }

        public static void SendNextInQueue()
        {
            if (alSendQueue.Count == 0)
                return;
            sendingFilePath = alSendQueue[0].ToString();

            frmLog.add_fromThread("Sending file: " + System.IO.Path.GetFileName(sendingFilePath), 5);
            SendToBoxie("INIT" + System.IO.Path.GetFileName(sendingFilePath));
        }
    }
}
